package com.nineclock.sign.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nineclock.common.entity.PageResult;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.threadlocal.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.common.utils.DateTimeUtil;
import com.nineclock.sign.dto.SignInfoDTO;
import com.nineclock.sign.dto.SignPCListDTO;
import com.nineclock.sign.excel.CustomStyle;
import com.nineclock.sign.mapper.SignInfoMapper;
import com.nineclock.sign.pojo.SignInfo;
import com.nineclock.sign.service.SignService;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.dto.SysDepartmentDTO;
import com.nineclock.system.feign.SysUserFeign;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class SignServiceImpl implements SignService {

    @Autowired
    private SignInfoMapper signInfoMapper;
    @Autowired
    private SysUserFeign sysUserFeign;

    @Override
    public PageResult<SignPCListDTO> queryMembers(String startTime, String endTime, Integer page, Integer pageSize) {
        if (StrUtil.isEmpty(startTime) || StrUtil.isEmpty(endTime)) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }

        List<Date> dateList = DateTimeUtil.getDatesBetweenTwoDate(startTime, endTime, DateTimeUtil.TIME_FORMAT_2);
        List<String> dateStrList = dateList.stream().map(date -> {
            return DateTimeUtil.dateToStr(date, DateTimeUtil.TIME_FORMAT_2);
        }).collect(Collectors.toList());


        LambdaQueryWrapper<SignInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(SignInfo::getCompanyUserId);
        queryWrapper.between(SignInfo::getCreateDate, startTime, endTime);
        queryWrapper.eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId());
        queryWrapper.groupBy(SignInfo::getCompanyUserId);
        Page<SignInfo> p = new Page<>(page, pageSize);
        p = signInfoMapper.selectPage(p, queryWrapper);
        List<SignInfo> list = p.getRecords();

        if (list == null && list.size() == 0) {
            return new PageResult<>();
        }

        List<SignPCListDTO> rows = new ArrayList<>();
        for (SignInfo info : list) {
            SysCompanyUserDTO sysCompanyUserDTO = sysUserFeign.queryByCompanyUserId(info.getCompanyUserId()).getData();
            SignPCListDTO signPCListDTO = new SignPCListDTO();
            signPCListDTO.setUsername(sysCompanyUserDTO.getUserName());
            signPCListDTO.setWorkNumber(sysCompanyUserDTO.getWorkNumber());
            signPCListDTO.setPost(sysCompanyUserDTO.getPost());

            SysDepartmentDTO sysDepartment = new SysDepartmentDTO();
            sysDepartment.setName(sysCompanyUserDTO.getDepartmentName());
            sysDepartment.setId(sysDepartment.getId());

            signPCListDTO.setDepartment(sysDepartment);

            LambdaQueryWrapper<SignInfo> qw = new LambdaQueryWrapper<>();
            qw.eq(SignInfo::getCompanyUserId, info.getCompanyUserId());
            qw.between(SignInfo::getCreateDate, startTime, endTime);
            List<SignInfo> signInfos = signInfoMapper.selectList(qw);
            Map<String, List<SignInfo>> map = signInfos.stream().collect(Collectors.groupingBy(SignInfo::getCreateDate));

            List<SignInfoDTO> signs = dateStrList.stream().map(dateStr -> {
                SignInfoDTO signInfoDTO = new SignInfoDTO();
                signInfoDTO.setSignDate(dateStr);

                List<SignInfo> signInfoList = map.get(dateStr);
                if (signInfoList != null && signInfoList.size() > 0) {
                    signInfoDTO.setSignCount(signInfoList.size());
                } else {
                    signInfoDTO.setSignCount(0);
                }
                return signInfoDTO;
            }).collect(Collectors.toList());

            signPCListDTO.setSigns(signs);
            rows.add(signPCListDTO);
        }


        return new PageResult<>(p.getTotal(), p.getPages(), rows);
    }

    //导出签到列表
    @Override
    public void exportSignData(String startTime, String endTime) throws IOException {
        //获取response对象 , 设置响应头信息
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        response.setHeader("Content-Disposition", "attachment;filename=attendance.xls");
        response.setContentType("application/octet-stream;charset=UTF-8");

        //查询指定时间范围内的考勤打卡数据
        List<SignPCListDTO> signPCListDTOList = this.querySignList(startTime, endTime, null, null).getRows();

        //通过EasyExcel的API生成文件, 并通过流的形式输出
        EasyExcel.write(response.getOutputStream())
                .excelType(ExcelTypeEnum.XLS)

                .registerWriteHandler(new CustomStyle())//设置列宽
                .registerWriteHandler(horizontalCellStyleStrategy())//设置字体样式

                .head(headers(signPCListDTOList))//表头
                .sheet("签到列表")
                .doWrite(getData(signPCListDTOList));//表数据
    }

    //获取表数据
    private List<List<String>> getData(List<SignPCListDTO> signPCListDTOS) {
        List<List<String>> dataList = new ArrayList<>();

        if (CollectionUtil.isNotEmpty(signPCListDTOS)) {
            signPCListDTOS.stream().forEach(signPCListDTO -> {
                List<String> rowList = new ArrayList<>();

                rowList.add(signPCListDTO.getUsername());
                rowList.add(signPCListDTO.getWorkNumber());
                rowList.add(signPCListDTO.getDepartment() == null ? "" : signPCListDTO.getDepartment().getName());
                rowList.add(signPCListDTO.getPost());

                List<SignInfoDTO> signs = signPCListDTO.getSigns();
                if (CollectionUtil.isNotEmpty(signs)) {
                    signs.stream().forEach(signInfoDTO -> {
                        rowList.add(signInfoDTO.getSignCount().toString());
                    });
                }
                dataList.add(rowList);
            });
        }

        return dataList;
    }

    //设置表头
    private List<List<String>> headers(List<SignPCListDTO> signPCListDTOS) {
        List<List<String>> headers = new ArrayList<>();
        headers.add(Arrays.asList("姓名"));
        headers.add(Arrays.asList("工号"));
        headers.add(Arrays.asList("部门"));
        headers.add(Arrays.asList("职位"));

        if (CollectionUtil.isNotEmpty(signPCListDTOS)) {
            SignPCListDTO signPCListDTO = signPCListDTOS.get(0);
            List<SignInfoDTO> signs = signPCListDTO.getSigns();

            if (CollectionUtil.isNotEmpty(signs)) {
                signs.stream().forEach(signInfoDTO -> {
                    headers.add(Arrays.asList(signInfoDTO.getSignDate()));
                });
            }
        }

        System.out.println("Headers: " + headers);
        return headers;
    }

    //设置样式
    public HorizontalCellStyleStrategy horizontalCellStyleStrategy() {
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //内容策略 - 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //设置字体
        WriteFont font = new WriteFont();
        font.setFontName("阿里巴巴普惠体");
        font.setFontHeightInPoints((short) 11);
        contentWriteCellStyle.setWriteFont(font);

        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(contentWriteCellStyle, contentWriteCellStyle);
        return horizontalCellStyleStrategy;
    }


    //查询签到列表
    public PageResult<SignPCListDTO> querySignList(String startTime, String endTime, Integer page, Integer pageSize) {
        //参数校验
        if (StrUtil.isEmpty(startTime) || StrUtil.isEmpty(endTime)) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }
        ArrayList<SignPCListDTO> signPCListDTOS = new ArrayList<>();

        //List<SignInfo> signInfos = signInfoMapper.selectList(null);
        //Map<Long, List<SignInfo>> userSignInfo = signInfos.stream().collect(Collectors.groupingBy(SignInfo::getCompanyUserId));

        List<SysCompanyUserDTO> data = sysUserFeign.queryAllCompanyUser().getData();
        Set<Long> collect1 = data.stream().map(sysCompanyUserDTO -> {
            return sysCompanyUserDTO.getId();
        }).collect(Collectors.toSet());
        //
        //for (Long userId : userSignInfo.keySet()) { //仅仅遍历signinfo表
        for (Long userId : collect1) {  //遍历所有员工表
            //封装signPCListDTO
            SysCompanyUserDTO sysCompanyUserDTO = sysUserFeign.queryByCompanyUserId(userId).getData();
            if (sysCompanyUserDTO != null) {
                SignPCListDTO signPCListDTO = BeanHelper.copyProperties(sysCompanyUserDTO, SignPCListDTO.class);
                signPCListDTO.setUsername(sysCompanyUserDTO.getUserName());
                //private SysDepartmentDTO department;
                SysDepartmentDTO departmentDTO = new SysDepartmentDTO();
                departmentDTO.setId(sysCompanyUserDTO.getCompanyId());
                departmentDTO.setName(sysCompanyUserDTO.getDepartmentName());
                signPCListDTO.setDepartment(departmentDTO);

                //private List<SignInfoDTO> signs;
                LambdaQueryWrapper<SignInfo> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(SignInfo::getCompanyUserId, sysCompanyUserDTO.getId());
                queryWrapper.between(SignInfo::getCreateDate, startTime, endTime);
                List<SignInfo> signInfos1 = signInfoMapper.selectList(queryWrapper);
                //signPCListDTO.setSigns(BeanHelper.copyWithCollection(signInfos1, SignInfoDTO.class));
                //封装signPCListDTO.setSigns
                Map<String, List<SignInfo>> collect = signInfos1.stream().collect(Collectors.groupingBy(SignInfo::getCreateDate));
                //遍历时间区间
                ArrayList<SignInfoDTO> signInfoDTOS = new ArrayList<>();
                Set<String> strings = collect.keySet();
                List<Date> datesBetweenTwoDate = DateTimeUtil.getDatesBetweenTwoDate(startTime, endTime, "yyyy-MM-dd");
                for (Date date : datesBetweenTwoDate) {
                    if (strings.contains(DateTimeUtil.dateToStr(date, "yyyy-MM-dd"))) {
                        SignInfoDTO signInfoDTO = new SignInfoDTO();
                        signInfoDTO.setSignDate(DateTimeUtil.dateToStr(date, "yyyy/MM/dd"));
                        signInfoDTO.setSignCount(collect.get(DateTimeUtil.dateToStr(date, "yyyy-MM-dd")).size());
                        signInfoDTOS.add(signInfoDTO);
                    } else {
                        SignInfoDTO signInfoDTO = new SignInfoDTO();
                        signInfoDTO.setSignDate(DateTimeUtil.dateToStr(date, "yyyy/MM/dd"));
                        signInfoDTO.setSignCount(0);
                        signInfoDTOS.add(signInfoDTO);
                    }
                }

                signPCListDTO.setSigns(signInfoDTOS);
                signPCListDTOS.add(signPCListDTO);
            }
        }


        if (page == null && pageSize == null) {
            return new PageResult<SignPCListDTO>(null, null, signPCListDTOS);
        } else {
            ArrayList<SignPCListDTO> signPCListDTOS1 = new ArrayList<>();
            for (int i = 0; i < signPCListDTOS.size(); i++) {
                int x = (page - 1) * pageSize;
                if (i >= x && i < x + pageSize) {
                    signPCListDTOS1.add(signPCListDTOS.get(i));
                }
            }
            return new PageResult<SignPCListDTO>((long) signPCListDTOS.size(), (long) (Math.ceil(signPCListDTOS.size() / pageSize) + 1), signPCListDTOS1);
        }
    }
}